package com.nuyoah.controller;


import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nuyoah.common.dto.ReviewDto;
import com.nuyoah.common.dto.UserDto;
import com.nuyoah.common.lang.Result;
import com.nuyoah.entity.*;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;



import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 活动相关接口
 *
 * @author xiaoming
 * @since 2024-03-26
 */
@RestController
@RequestMapping("/sys/activity")
public class ActivityController extends BaseController {


    /**
     * 游客获取列表。
     *
     * @param keyword 可选参数，用于活动标题或内容的模糊搜索。
     * @return 返回操作结果，包含分页的活动列表。
     */
    @GetMapping("/list/user")
    public Result listUser(@RequestParam(required = false) String keyword) {
        // 创建查询条件，初始设置为状态为审核通过的活动
        QueryWrapper<Activity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", 1);

        // 如果提供了关键字，则在标题或内容中进行模糊搜索
        if (StringUtils.isNotBlank(keyword)) {
            queryWrapper.and(wrapper -> wrapper
                    .like("title", keyword)  // 根据标题进行模糊查询
                    .or()
                    .like("context", keyword));  // 或者根据内容进行模糊查询
        }

        // 执行查询并返回结果
        return Result.succ(activityService.page(getPage(), queryWrapper));
    }



    /**
     * 获取活动列表的接口。
     * 本接口用于获取活动的分页列表，包括分页信息和每个活动的详细信息。
     * 通过调用活动服务层的分页查询方法，根据创建时间降序排列活动列表。
     *
     * @param keyword 可选参数，用于活动标题和内容的模糊搜索。
     * @return Result 包含活动列表的查询结果，其中也含有分页信息。
     */
    @GetMapping("/list")
    @PreAuthorize("hasAuthority('sys:activity:list')")
    public Result list(@RequestParam(required = false) String keyword) {
        // 构造查询条件
        QueryWrapper<Activity> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("created");  // 按创建时间降序排序

        // 如果有关键词，则添加到查询条件中进行模糊搜索
        if (StringUtils.isNotBlank(keyword)) {
            queryWrapper.and(wrapper -> wrapper
                    .like("title", keyword)  // 根据标题进行模糊查询
                    .or()
                    .like("context", keyword));  // 或者根据内容进行模糊查询
        }

        // 调用活动服务层进行分页查询
        Page<Activity> page = activityService.page(getPage(), queryWrapper);

        // 返回查询结果
        return Result.succ(page);
    }



    /**
     * 查看指定活动的详细信息。
     *
     * @param id 活动的唯一标识符，从URL路径变量中获取。
     * @return 返回活动的详细信息，如果活动不存在，则返回错误信息。
     */
    @GetMapping("/view/{id}")
    @PreAuthorize("hasAuthority('sys:activity:view')")
    public Result view(@PathVariable Long id) {
        // 通过活动ID获取活动信息
        Activity activity = activityService.getById(id);
        // 确保活动存在，否则抛出异常
        Assert.notNull(activity, "未找到指定活动");
        return Result.succ(activity);
    }


    /**
     * 上传图片
     *
     * @param file 用户上传的图片文件
     * @return 返回上传结果，成功返回文件名，失败返回错误信息
     * @throws IOException 如果发生IO异常
     */
    @PostMapping("/uploadImage")
    public Result uploadimg(MultipartFile file) throws IOException {
        // 定义图片存储目录
        String folder = "D:/code/platform/file/img";
        // 创建文件夹对象
        File imgFolder = new File(folder);
        // 获取原始文件名
        String fname = file.getOriginalFilename();
        // 提取文件扩展名
        String ext = "." + fname.substring(fname.lastIndexOf(".") + 1);
        // 生成时间戳加UUID的新型文件名以避免重复
        String dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
        String newFileName = dateTimeFormatter + UUID.randomUUID().toString().replaceAll("-", "") + ext;
        // 指定文件在本地的完整路径
        File filePath = new File(imgFolder, newFileName);
        // 如果文件夹不存在，则创建文件夹
        if (!filePath.getParentFile().exists()) {
            filePath.getParentFile().mkdirs();
        }
        try {
            // 将上传文件保存到本地
            file.transferTo(filePath);
            // 返回成功结果，包含保存的文件名
            return Result.succ(filePath.getName());
        } catch (IOException e) {
            // 文件保存失败，打印异常并返回失败结果
            e.printStackTrace();
            return Result.fail("上传图片失败");
        }
    }


    /**
     * 新增活动。
     * 该接口使用POST请求方法，通过验证用户权限后，允许用户保存一个新的活动信息。
     * 同时，该接口会记录创建该活动的用户，将相关信息存储到数据库中。
     *
     * @param activity 通过RequestBody接收的活动对象，需经过验证确保数据的有效性。
     * @param request  用户的请求信息，用于辅助处理请求。
     * @return 返回操作结果，如果操作成功，则返回成功的状态和保存的活动信息；如果操作失败，则返回失败的状态和错误信息。
     * @throws IOException 如果发生IO异常。
     */
    @PostMapping("/save")
    @PreAuthorize("hasAuthority('sys:activity:save')")
    public Result save(@Validated @RequestBody Activity activity, HttpServletRequest request) throws IOException {
        // 保存活动信息
        activityService.save(activity);

        // 从SecurityContextHolder获取当前认证用户的名称
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        // 根据用户名获取用户信息
        User user = userService.getByUsername(username);
        if (user == null) {
            // 如果用户不存在，返回失败结果
            return Result.fail("未找到用户");
        }
        // 获取用户ID
        Long userId = user.getId();
        // 在activity_user表中保存活动和用户的关联信息
        activityUserService.save(new ActivityUser(activity.getId(), userId));

        // 返回成功结果，包含保存的活动信息
        return Result.succ(activity);
    }


    /**
     * 更新活动信息。
     *
     * @param id       活动的唯一标识符，从URL路径变量获取。
     * @param activity 包含更新后活动信息的对象，通过RequestBody接收。
     * @return 返回操作结果，如果成功更新，则返回更新后的活动信息。
     * @author System
     * @since 1.0.0
     */
    @PostMapping("/update/{id}")
    @PreAuthorize("hasAuthority('sys:activity:update')")
    public Result update(@PathVariable Long id, @Validated @RequestBody Activity activity) {

        // 根据ID获取活动，为更新做准备
        Activity oldActivity = activityService.getById(id);
        Assert.notNull(oldActivity, "未找到指定活动");

        // 更新活动信息
        oldActivity.setTitle(activity.getTitle());
        oldActivity.setContext(activity.getContext());
        oldActivity.setCreated(activity.getCreated());
        oldActivity.setAddress(activity.getAddress());
        oldActivity.setLimitCount(activity.getLimitCount());
        oldActivity.setImage(activity.getImage());
        oldActivity.setStartTime(activity.getStartTime());
        oldActivity.setEndTime(activity.getEndTime());
        oldActivity.setUpdated(LocalDateTime.now());

        // 保存更新后的活动信息到数据库
        activityService.updateById(oldActivity);

        // 返回更新成功的结果
        return Result.succ(oldActivity);
    }


    /**
     * 删除活动
     *
     * @param ids 活动ID数组，通过RequestBody接收前端传来的多个活动ID
     * @return 返回操作结果，如果删除成功，则返回成功消息
     */
    @PostMapping("/delete")
    @PreAuthorize("hasAuthority('sys:activity:delete')")
    public Result delete(@RequestBody Long[] ids) {
        // 批量删除活动
        activityService.removeByIds(Arrays.asList(ids));
        //删除关联表activity_user中的记录
        activityUserService.remove(new QueryWrapper<ActivityUser>().in("activity_id", Arrays.asList(ids)));
        // 返回删除成功的消息
        return Result.succ("活动删除成功");
    }


    /**
     * 对指定活动进行审核
     *
     * @param id 活动的ID，不能为空
     * @return 返回审核结果，成功则返回消息"活动审核成功"
     */
    @PostMapping("/audit/{id}")
    @PreAuthorize("hasAuthority('sys:activity:audit')")
    public Result audit(@PathVariable Long id) {
        // 通过ID获取活动信息
        Activity activity = activityService.getById(id);
        // 检查活动是否存在
        Assert.notNull(activity, "未找到指定活动");
        // 设置活动为审核通过状态
        activity.setStatus(1); // 假设1为审核通过状态
        // 更新活动状态
        activityService.updateById(activity);
        // 返回审核成功消息
        return Result.succ("活动审核成功");
    }

    /**
     * 审核活动驳回接口
     *
     * @param id 活动的ID，用于指定需要审核的活动
     * @param reason 审核驳回的原因
     * @return 返回审核驳回操作的结果信息
     */
    @PostMapping("/auditBat/{id}")
    @PreAuthorize("hasAuthority('sys:activity:audit')")
    public Result auditBat(@PathVariable Long id, @RequestBody String reason) {
        // 通过ID获取活动信息
        Activity activity = activityService.getById(id);

        // 检查活动是否存在
        Assert.notNull(activity, "未找到指定活动");

        // 设置活动为审核驳回状态
        activity.setStatus(2); // 假设2为审核驳回状态

        activity.setReason(reason);
        // 更新活动状态
        activityService.updateById(activity);

        // 返回审核成功消息
        return Result.succ("活动驳回成功");
    }


    /**
     * 导出报名信息的接口。
     *
     * @param response 用于将报名信息导出为Excel文件并返回给客户端的HttpServletResponse对象。
     * @param id       活动的ID，用于查询对应的报名信息。
     *                 <p>
     *                 注意：此接口需要用户具有'sys:activity:export'权限才能访问。
     *                 对于非管理员和非超级管理员用户，只有当其是活动的创建者或报名者时，才能导出活动的报名信息。
     */
    @GetMapping("/export/{id}")
    //@PreAuthorize("hasAuthority('sys:activity:export')")
    public void export(HttpServletResponse response,@PathVariable Long id) throws Exception {

        // 从数据库查询出所有的数据
        // 获取报名信息,根据活动ID从报名表registration中查询所有该id的记录
        List<Registration> registrations = registrationService.getByActivityId(id);
        if (registrations == null || registrations.isEmpty()) {
            return;
        }
        //根据活动id获取活动标题
        String title = activityService.getById(id).getTitle();
        List<UserDto> list = userService.getUserDto(registrations);
        // 通过工具类创建writer 写出到磁盘路径
//        ExcelWriter writer = ExcelUtil.getWriter(filesUploadPath + "/用户信息.xlsx");
        // 在内存操作，写出到浏览器
        ExcelWriter writer = ExcelUtil.getWriter(true);
        //自定义标题别名
        writer.addHeaderAlias("studentId", "学号");
        writer.addHeaderAlias("name", "姓名");
        writer.addHeaderAlias("username", "用户名");
        writer.addHeaderAlias("time", "报名时间");

        // 一次性写出list内的对象到excel，使用默认样式，强制输出标题
        writer.write(list,true);

        // 设置浏览器响应的格式
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode(title+"活动报名名单", "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        out.close();
        writer.close();
    }

    /**
     * 对活动进行评价
     *
     * @param reviewDto 包含评价内容的对象，其中必须包含activity_user_id以标识评价的活动用户
     * @return 返回一个结果对象，如果评价成功，则包含成功消息
     */
    @PostMapping("/evaluate")
    @PreAuthorize("hasAuthority('sys:activity:evaluate')")
    public Result evaluateActivity(@Validated @RequestBody ReviewDto reviewDto) {
        try {
            // 通过Spring Security获取当前登录的用户名，并查询该用户的信息
            String username = SecurityContextHolder.getContext().getAuthentication().getName();
            User user = userService.getByUsername(username);
            if (user == null) {
                return Result.fail("未找到用户");
            }
            Long userId = user.getId();

            //根据reviewDto中的id，这里的id是activity_id
            //然后根据当前用户的id和activity_id到registration表中查询出activity_id
            Registration registration = registrationService.getByActivityIdAndUserId(reviewDto.getId(), userId);
            if (registration == null) {
                return Result.fail("未找到活动报名信息");
            }

            Review review = new Review();
            review.setRegistrationId(registration.getActivityId());
            review.setScore(reviewDto.getScore());
            review.setEvaluation(reviewDto.getEvaluation());
            review.setTime(LocalDateTime.now());

            // 将审核信息（review）保存到数据库中
            reviewService.save(review);
            return Result.succ("活动评价成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.fail("活动评价失败");
        }
    }

    /**
     * 报名活动
     *
     * @param id 活动的ID，用于标识要报名的活动
     * @return 返回报名结果，成功返回Result.succ("报名成功")，失败则返回相应的错误信息
     */
    @PostMapping("/signup/{id}")
    @PreAuthorize("hasAuthority('sys:activity:application')")
    public Result signup(@PathVariable Long id) {
        // 根据ID获取活动信息
        Activity activity = activityService.getById(id);
        Assert.notNull(activity, "未找到指定活动");

        // 检查活动报名人数是否已满
        if (activity.getLimitCount() <= 0) {
            return Result.fail("报名人数已满");
        }

        if (activity.getStatus() == 2) {
            return Result.fail("活动已被驳回，无法报名");
        }

        if(activity.getStatus() == 0){
            return Result.fail("活动还未审核通过，无法报名");
        }

        // 检查当前时间是否在活动允许的时间范围内
        LocalDateTime now = LocalDateTime.now();
        if (activity.getStartTime().isAfter(now) || activity.getEndTime().isBefore(now)) {
            return Result.fail("活动未开始或已结束");
        }

        // 通过Spring Security获取当前登录的用户名，并查询该用户的信息
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        User user = userService.getByUsername(username);
        if (user == null) {
            return Result.fail("未找到用户");
        }
        Long userId = user.getId();

        // 检查用户是否已经报名了当前活动
        boolean isRegistered = registrationService.isRegistered(userId, id);
        if (isRegistered) {
            return Result.fail("您已经报名了此活动");
        }

        // 执行报名流程
        try {
            // 创建报名记录并保存到数据库
            Registration registration = new Registration();
            registration.setUserId(userId);
            registration.setActivityId(id);
            registrationService.save(registration);

            // 更新活动报名人数
            activity.setLimitCount(activity.getLimitCount() - 1);
            activityService.updateById(activity);

            return Result.succ("报名成功");
        } catch (Exception e) {
            // 报名过程中发生异常，返回错误信息
            return Result.fail("报名过程中发生错误，请稍后重试");
        }
    }

    /**
     * 获取统计数据
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 统计数据的结果
     */
    @PostMapping("/statistics")
    @PreAuthorize("hasAuthority('sys:activity:statistics')")
    public Result statistics(@RequestParam String startTime, @RequestParam String endTime) {
        LocalDateTime start = LocalDateTime.parse(startTime, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        LocalDateTime end = LocalDateTime.parse(endTime, DateTimeFormatter.ISO_LOCAL_DATE_TIME);

        // 创建查询条件：活动结束时间在startTime和endTime之间
        QueryWrapper<Activity> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("end_time", start, end);

        // 查询符合条件的活动
        List<Activity> activities = activityService.list(queryWrapper);

        // 活动ID集合，用于查询报名信息
        List<Long> activityIds = activities.stream().map(Activity::getId).collect(Collectors.toList());

        // 查询这些活动的报名信息
        Map<Long, List<Registration>> registrationsMap = registrationService.list(new QueryWrapper<Registration>()
                        .in("activity_id", activityIds))
                .stream()
                .collect(Collectors.groupingBy(Registration::getActivityId));

        // 寻找报名人数最多的活动
        Activity mostJoinedActivity = null;
        int maxRegistrations = 0;
        for (Activity activity : activities) {
            List<Registration> registrations = registrationsMap.getOrDefault(activity.getId(), new ArrayList<>());
            int count = registrations.size();
            if (count > maxRegistrations) {
                maxRegistrations = count;
                mostJoinedActivity = activity;
            }
        }

        // 寻找参加活动次数最多的用户
        Map<Long, Long> userActivitiesCount = registrationsMap.values().stream()
                .flatMap(List::stream)
                .collect(Collectors.groupingBy(Registration::getUserId, Collectors.counting()));

        Long mostActiveUserId = userActivitiesCount.entrySet().stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey)
                .orElse(null);

        User mostActiveUser = userService.getById(mostActiveUserId);

        // 把统计结果打包返回
        Map<String, Object> statistics = new HashMap<>();
        statistics.put("mostJoinedActivity", mostJoinedActivity);
        statistics.put("mostActiveUser", mostActiveUser);
        statistics.put("totalActivitiesCount", activities.size());
        statistics.put("totalRegistrationsCount", userActivitiesCount.values().stream().reduce(0L, Long::sum));

        return Result.succ(statistics);
    }

}
